Writting a blog with Emacs and Org Mode
Explaining my blogging setup with Org Mode.

Table of Contents

Introduction

Finally got around creating a blog. I have been wanting to create one for quite some time, but I never found the perfect setup and never developed a consistent writing habit. Hopefully that will change now. I mostly want some place where I can write about the topics that I am learning at any given time, mainly related software and programming, since a lot of the concepts on these fields are quite complex, and making the effort to structure my knowledge on a blog post will most likely help me speed up my learning and improve my understanding on these areas.

I use Emacs as my main editor, and so I want to write my posts using Org Mode, since it has a lot of cool features. I am specifically interested in the ability to work with source code within the blog files, because I expect that I will be writing a lot about programming stuff.

At first, I was looking for static web site generators that could understand Org files, but then I found out about the Org Mode publishing functionality and decided to give it a try. Besides the official org-mode docs, this page was extremely helpful in understanding how this all works.

I don't think my writing is great, but hopefully this is something that will improve over-time. Any feedback on this regard would be very welcome, if you happen to come across any of my posts.

Project setup

Org Publish config

The general setup consists of using the sitemap generated automatically by org-publish to create a list of all my posts in the sitemap.org file. All posts are stored within a Posts folder. Part of the sitemap is then inserted into the index.org file using INCLUDE directive:

#+INCLUDE: sitemap.org :lines "5-"

This is what my org-publish config looks like:

(setq org-export-global-macros '(("timestamp" . "@@html:<span class=\"timestamp\">[$1]</span>@@")))

(defun my/org-sitemap-date-entry-format (entry style project)
  "Format ENTRY in org-publish PROJECT Sitemap format ENTRY ENTRY STYLE format that includes date."
  (let ((filename (org-publish-find-title entry project)))
(if (= (length filename) 0)
    (format "*%s*" entry)
  (format "{{{timestamp(%s)}}} [[file:%s][%s]]"
      (format-time-string "%Y-%m-%d" (org-publish-find-date entry project))
      entry
      filename))))

(setq org-publish-project-alist
  `(
    ("org-posts"
     :base-directory ,base-blog-directory
     :base-extension "org"
     :publishing-directory ,base-blog-directory-public 
     :recursive t
     :publishing-function org-html-publish-to-html
     :headline-levels 4             ; Just the default for this project.
     :auto-preamble nil
     :auto-postamble nil
     :table-of-contents nil
     :auto-sitemap t
     :section-numbers nil
     :sitemap-filename "sitemap.org"
     :sitemap-title "Trying to Code"
     :sitemap-format-entry my/org-sitemap-date-entry-format
     :html-head "<link rel=\"stylesheet\" href=\"../style.css\" type=\"text/css\"/>"
     :html-preamble "<nav> <a href=\"/\">Home</a> </nav> "
     :html-postamble "<footer> <div id=\"updated\"> %C</div> </footer>"
     )

    ("org-static"
     :base-directory ,base-blog-directory-static
     :base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf"
     :publishing-directory ,base-blog-directory-public
     :recursive t
     :publishing-function org-publish-attachment
     )

    ("blog" :components ("org-posts" "org-static"))
)
)

Additionally, I also wrote a few helper functions to allow me to quickly jump and start editing a new or an unfinished post.

 (defun get-org-files-dir (folder)
  "Return a list of all org files in a directory"
  (-filter (lambda (s) (string-match "org" s)) (directory-files folder))
 )

(defun open-blog ()
  "Select one of the posts on my blog, or create a new one"
  (interactive)
  (let (
        (f (file-name-with-extension (completing-read "Select post to Edit" (get-org-files-dir base-blog-directory-posts))  "org" ))
        (f_path)
        (file_exists)
        )
  (setq f_path (expand-file-name f base-blog-directory-posts))
  (setq file_exists (file-exists-p f_path))

  (find-file f_path)

  ;; Apend the title to the file
  (when (file_exists)
  (with-current-buffer f (insert (format "#+TITLE: %s\n" (file-name-base f)) ))
  (with-current-buffer f (insert "#+DESCRIPTION:" ))
    )

  )
)

When I call the open-blog interactive function, it prompts me to either select one of the pre-existing posts, or create a new one.

Deployment Setup

When I call the org-publish command, it will publish all the html and static assets into the public folder. This folder is itself a submodule, which has an origin on codeberg, which I use to host the blog.

I created a Makefile target to commit and push everything on the submodule, making it very easy to publish when I make changes on the blog.

I also shamelessly copied my css stylesheet from here.

Features to add

I am overall very happy with my current workflow, however there are a few things that I will be working on:

  • [X] Add the date to the sitemap post list.
  • [X] Improve the css and general styling.
  • [ ] Improve the highlighting on code blocks.
  • [ ] Incorporate some sort of analytics.
  • [ ] Maybe create a tag system?
  • [ ] Try to improve the SEO. I know next to anything about this topic.